﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;
using System.Reflection;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using System.Globalization;
using Microsoft.SharePoint.Workflow;
using Microsoft.SharePoint.ApplicationPages;
using Microsoft.SharePoint.Administration;

namespace Components2.JobManager {

    public static class SPJobDefinitionExt {

        public static readonly string CLONE = "(clone)";

        //cant change the schedule on the job
        //error: "Cannot change a recurring schedule to a one-time schedule (and vice versa) for a job definition"
        //i dont delete and recreate, as it might break some existing internal jobs
        //better to keep the existing jobs, and copy them
        //using reflection too hard to copy jobs, jobs have multiple constructors
        //can do a normal clone, as it keeps the same id 
        //error: "Objects cannot be used once they have been deleted"

        public static void CloneFull(this SPJobDefinition job, bool onetime) {

            SPJobDefinition newjob = (SPJobDefinition)job.Clone();
            Guid newid = Guid.NewGuid();
            newjob.Id = newid;
            
            List<FieldInfo> fields = new List<FieldInfo>();
            Type type = job.GetType();
            fields.AddRange(type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.Public));
            while (type != null) {
                fields.AddRange(type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic));
                type = type.BaseType;
            }
            foreach (FieldInfo fi in fields) {
                if (fi.Name == "m_Version") { //need to set this property to force a new item
                    fi.SetValue(newjob, -1); //SPConstants.InvalidRowVersion
                }
            }

            string newtitle = job.Title + " " + CLONE + " " + Guid.NewGuid();
            newjob.Name = newtitle;
            newjob.Title = newtitle;
            if (onetime) {
                newjob.Schedule = new SPOneTimeSchedule(DateTime.Now.AddHours(-2));
            } else {
                SPMinuteSchedule schedule = new SPMinuteSchedule();
                schedule.BeginSecond = DateTime.Now.Second + 10;
                if (schedule.BeginSecond > 60) {
                    schedule.BeginSecond -= 60;
                }
                schedule.EndSecond = schedule.BeginSecond + 30;
                if (schedule.EndSecond > 60) {
                    schedule.EndSecond -= 60;
                }
                schedule.Interval = 1;
                newjob.Schedule = schedule;
            }
            newjob.Update();

            //need this to force the existing object to refresh from the database
            //otherwise the UI thinks it is the cloned job
            job.Uncache();

            if (onetime) {
                bool exists = true;
                while (exists) {
                    SPFarm local = SPFarm.Local;
                    job = (SPJobDefinition)local.GetObject(newid);
                    exists = (job != null);
                    System.Threading.Thread.Sleep(500);
                }
            } else {
                while (newjob.LastRunTime == DateTime.MinValue) {
                    //reload from the database
                    newjob.Uncache();
                    System.Threading.Thread.Sleep(500);
                }
                //only want to run the job once
                newjob.IsDisabled = true;
                newjob.Update();
            }
        }
    }
}